package com.snap.vseries.analyze;


import com.snap.vseries.bean.AnalysisProtocol;
import com.snap.vseries.bean.ChannelBean;

import org.apache.commons.math3.stat.StatUtils;

import java.util.Arrays;
import java.util.List;

public class BaselineAb {

    private static final String TAG = "BaselineAb";

    private static final double minPeakThreshold = 0;
    private static final double minPeakHeight = 0.001D;
    private static final double minSlope = 0.004D;

    private static final SavitzkyGolay sg702 = new SavitzkyGolay(7, 0, 2);
    private static final SavitzkyGolay sg722 = new SavitzkyGolay(7, 2, 2);

    public static void doAnalyze(List<ChannelBean> channelBeans, AnalysisProtocol analysisProtocol, AlgoParams algoParams) {
        int defaultBaselineStart = algoParams.getDefBaselineStart();
        for (int i = 0; i < channelBeans.size(); i++) {
            ChannelBean channelBean = channelBeans.get(i);
            if (channelBean == null || channelBean.getOrigin() == null || channelBean.getOrigin().length == 0) {
                continue;
            }
            if (channelBean.getOrigin().length < 6) {
                continue;
            }
            int numCycles = channelBean.getOrigin().length;
            double[] signal = new double[numCycles];
            for (int j = 0; j < numCycles; j++) {
                signal[j] = channelBean.getOrigin()[j];
            }
            //归一化到基线为2.x 或 1.x
            double avg;
            if (numCycles < 10) {
                avg = StatUtils.mean(signal);
            } else {
                avg = StatUtils.mean(signal, 5, 5);
            }
            for (int j = 0; j < numCycles; j++) {
                signal[j] = signal[j] / avg;
            }
            System.out.println("魔法化后的信号:" + Arrays.toString(signal));

            int maxIndex = 0;

            double[] signalD2 = sg722.applyFilter(signal);
            double[] smoothedSignalD2 = sg702.applyFilter(signalD2);
            System.out.println(Arrays.toString(smoothedSignalD2));

            double[] maxima = findMaxima(smoothedSignalD2, minPeakHeight, signal);
            if (maxima == null) {
                maxIndex = numCycles - 1;
                System.out.println("NO_PEAK_FOUND");
            } else {
                double maxValue = Double.MIN_VALUE;
                for (int j = 0; j < maxima.length; j++) {
                    if (maxima[j] > maxValue) {
                        maxValue = maxima[j];
                        maxIndex = j;
                    }
                }
                if (maxValue < minPeakThreshold) {
                    maxIndex = numCycles - 1;
                    System.out.println("NO_PEAK_ABOVE_THRESHOLD");
                }
            }
            int expRegionBottom = numCycles - 1;
//            int expRegionTop = maxIndex;
            if (maxIndex > 0) {
                int loc = 1;

                double slope = (signal[maxIndex] - signal[defaultBaselineStart - 1]) / (maxIndex - defaultBaselineStart + 1);
                double evaSlope = Math.max(slope, minSlope);
                while ((signal[(maxIndex - loc + 1)] - signal[(maxIndex - loc)] > evaSlope) && loc < 6) {
                    loc++;
                }
                System.out.println("slope:" + slope + " , evaSlope:" + evaSlope + ", pre len:" + loc);
                //int expRegionBottomIndex=maxIndex-loc;
                expRegionBottom = maxIndex - loc + 1;
            } else {
                expRegionBottom = numCycles - 1;
//                expRegionTop = numCycles - 1;
                System.out.println("NOT_VALID_PEAK_LOCATION");
            }
            System.out.println("expBottom:" + expRegionBottom + ", maxIndex:" + maxIndex);

            int baselineStart = defaultBaselineStart;
            int baselineEnd = Math.max(10, expRegionBottom);

            channelBean.setBlStart(baselineStart);
            channelBean.setBlStop(baselineEnd);
        }
    }

    public static double[] findMaxima(double[] x, double minPeakHeight, double[] signal) {
        int len = x.length;
        int[] com_ind = new int[len]; //原长度
        double[] x1 = new double[len - 2];//减去2个长度的数组、  存放的后减前的差值

        int i;
        for (i = 0; i < x1.length; ++i) {
            x1[i] = x[i + 1] - x[i];
        }

        for (i = 0; i < x1.length; ++i) {
            if (x1[i] >= 0.0D //差值是正 代表2阶导数还在增长 并且
                    && signal[i + 1] > signal[i]) {
                com_ind[i + 1] = 1;
            }
        }

        for (i = 0; i < x1.length; ++i) {
            x1[i] = x[i + 1] - x[i + 2];
        }

        for (i = 0; i < x1.length; ++i) {
            if (x1[i] >= 0.0D && signal[i + 2] > signal[i + 1]) {
                ++com_ind[i + 1];
            }
        }

        for (i = 0; i < len; ++i) {
            if (Math.abs(x[i]) > minPeakHeight) {
                com_ind[i]++;
            }
        }

        boolean hasMaxima = false;
        double[] maxima = new double[len];

        for (i = 0; i < len; ++i) {
            if (com_ind[i] == 3) {
                maxima[i] = x[i];
                hasMaxima = true;
            }
        }

        if (hasMaxima) {
            return maxima;
        } else {
            return null;
        }
    }

//    private static double[] polyval(double[] coefs, double[] xs) {
//        double[] ys = new double[xs.length];
//        PolynomialFunction function = new PolynomialFunction(coefs);
//        for (int i = 0; i < ys.length; i++) {
//            ys[i] = function.value(xs[i]);
//        }
//        return ys;
//    }

//    private static double[] polyfit(double[] xs, double[] ys, int n) {
//        WeightedObservedPoints points = new WeightedObservedPoints();
//        for (int i = 0; i < xs.length; i++) {
//            points.add(xs[i], ys[i]);
//        }
//        PolynomialCurveFitter fitter = PolynomialCurveFitter.create(n);
//        return fitter.fit(points.toList());
//    }
}
